home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 4 / Info_Mac IV CD-ROM (Pacific HiTech Inc.)(August 1994).iso / Development / Source / tarsrc Folder / extract.c < prev    next >
Text File  |  1994-02-02  |  9KB  |  407 lines

  1. /*
  2.  * Macintosh Tar
  3.  *
  4.  * Modified by Craig Ruff for use on the Macintosh.
  5.  */
  6. /*
  7.  * Extract files from a tar archive.
  8.  *
  9.  * Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu.
  10.  *
  11.  * @(#) extract.c 1.17 86/10/29 Public Domain - gnu
  12.  */
  13. #include "tar.h"
  14.  
  15. extern union record    *head;        /* Points to current tape header */
  16. extern struct stat {
  17.     long    st_size;
  18.     long    st_mtime;
  19. } hstat;                /* Fake stat struct for compat. */
  20.  
  21. Boolean ExtractArchive(), NewName(char *name, int retries);
  22. extern void PrintHeader();
  23. extern Boolean SkipFile();
  24.  
  25. int MakeDirs();            /* Makes required directories */
  26.  
  27. /*
  28.  * Extract - extract the entire archive
  29.  */
  30. Extract() {
  31.     if (OpenArchive("\pExtract Archive:", true))
  32.         return;
  33.         
  34.     if (GetDir("\pExtraction Directory:", true) == false)
  35.         return;
  36.  
  37.     /*
  38.      * Extract and print the files as found in the archive.
  39.      */
  40.     if (!WindInit()) {
  41.         TextFace(underline);
  42.         WPrintf(header);
  43.         TextFace(0);
  44.         ReadAnd(ExtractArchive);
  45.         CloseArchive();
  46.         WindEnd(pref.autoPage);
  47.     }
  48.     
  49.     RlsDir();
  50. }
  51.  
  52. /*
  53.  * Extract a file from the archive.
  54.  */
  55. Boolean
  56. ExtractArchive()
  57. {
  58.     register char    *data;
  59.     register char    *s, *d;
  60.     union record    *rec;
  61.     int        i, namelen;
  62.     Boolean        errFound = false;
  63.     Boolean        crSeen;
  64.     long        bytes, check, written;
  65.     long        size;
  66.     OSErr        err;
  67.     char        macName[256];
  68.     int        numRetries = 0;
  69.     HParamBlockRec    dpb;
  70.     HParamBlockRec    fpb;
  71.     char        *routine = "\pExtractArchive";
  72.  
  73.     SaveRec(&head);            /* Make sure it sticks around */
  74.     UseRec(head);            /* And go past it in the archive */
  75.     DecodeHeader(head, &hstat, 1);    /* Snarf fields */
  76.  
  77.     /* Print the record from 'head' and 'hstat' */
  78.     PrintHeader();
  79.  
  80.     switch (head->header.linkflag) {
  81.     default:
  82.         WPrintf("Unknown file type %d for %s",
  83.             head->header.linkflag, head->header.name);
  84.         break;
  85.  
  86.     case LF_OLDNORMAL:
  87.     case LF_NORMAL:
  88.         /*
  89.          * Appears to be a file.
  90.          * See if it's really a directory.
  91.          */
  92.         namelen = strlen(head->header.name) - 1;
  93.         if (head->header.name[namelen] == '/')
  94.             goto really_dir;
  95.  
  96.         FixName(head->header.name, macName);
  97.     again_file:
  98.         memset(&fpb, 0, sizeof(fpb));
  99.         fpb.fileParam.ioCompletion = nil;
  100.         fpb.fileParam.ioNamePtr = macName;
  101.         fpb.fileParam.ioVRefNum = dirVRefNum;
  102.         fpb.fileParam.ioFVersNum = 0;
  103.         fpb.fileParam.ioDirID = 0;
  104.         err = PBHCreate(&fpb, false);
  105.         if (
  106.             (err == noErr) ||
  107.             ((err == dupFNErr) && pref.doOverWrite)
  108.         ) {
  109.  
  110.             fpb.fileParam.ioCompletion = nil;
  111.             fpb.fileParam.ioNamePtr = macName;
  112.             fpb.fileParam.ioVRefNum = dirVRefNum;
  113.             fpb.fileParam.ioFVersNum = 0;
  114.             fpb.fileParam.ioDirID = 0;
  115.             fpb.ioParam.ioPermssn = fsWrPerm;
  116.             fpb.ioParam.ioMisc = nil;
  117.             err = PBHOpen(&fpb, false);
  118.         }
  119.  
  120.         if (err != noErr) {
  121.             if (
  122.                 (err == dupFNErr) &&
  123.                 NewName(macName, numRetries++)
  124.             ) {
  125.                 goto again_file;
  126.             }
  127.             
  128.             if (MakeDirs(macName))
  129.                 goto again_file;
  130.  
  131.             PgmAlert(routine, "\pCould not make file", macName);
  132.             errFound = SkipFile((long)hstat.st_size);
  133.             break;
  134.         }
  135.  
  136.         fpb.ioParam.ioMisc = 0;
  137.         if ((err = PBSetEOF((ParmBlkPtr) &fpb, false)) != noErr) {
  138.             OSAlert(routine, "\pPBSetEOF", macName, err);
  139.         }
  140.         
  141.         /*
  142.          * Note that this only extracts data forks!
  143.          */
  144.         if (pref.dosCvt)
  145.             crSeen = false;
  146.             
  147.         for (size = hstat.st_size;
  148.              size > 0;
  149.              size -= written) {
  150.             /*
  151.              * Locate data, determine max length
  152.              * writeable, write it, record that
  153.              * we have used the data, then check
  154.              * if the write worked.
  155.              */
  156.             if ((rec = FindRec()) == nil)
  157.                 return(true);
  158.                 
  159.             data = rec->charptr;
  160.             written = EndOfRecs()->charptr - data;
  161.             if (written > size)
  162.                 written = size;
  163.  
  164.             bytes = written;
  165.             if (pref.cvtNl) {
  166.                 /*
  167.                  * Convert newlines to return for Mac compatability.
  168.                  */
  169.                 for (i = bytes, d = data; --i >= 0; d++)
  170.                     if (*d == LF)
  171.                         *d = RETURN;
  172.                         
  173.             } else if (pref.dosCvt) {    
  174.                 /*
  175.                  * Convert DOS Style CR/LF to CR only.
  176.                  */
  177.                 for (s = d = data, i = bytes; i > 0; i--) {
  178.                      if ((*s != LF) || !crSeen)
  179.                         *d++ = *s;
  180.                         
  181.                     crSeen = *s++ == RETURN;
  182.                 }
  183.                 
  184.                 bytes -= s - d;
  185.             }
  186.  
  187.             check = bytes;
  188.             fpb.ioParam.ioBuffer = data;
  189.             fpb.ioParam.ioReqCount = check;
  190.             fpb.ioParam.ioPosMode = fsAtMark;
  191.             fpb.ioParam.ioPosOffset = 0;
  192.             err = PBWrite((ParmBlkPtr) &fpb, false);
  193.             if (err != noErr) {
  194.                 OSAlert(routine, "\pPBWrite", macName, err);
  195.                 goto doNext;
  196.             }
  197.  
  198.             check = fpb.ioParam.ioActCount;
  199.             /*
  200.              * The following is in violation of strict
  201.              * typing, since the arg to userec
  202.              * should be a struct rec *.  FIXME.
  203.              */
  204.             UseRec(data + written - 1);
  205.             if (check == bytes)
  206.                 continue;
  207.                 
  208.             /*
  209.              * Error in writing to file.
  210.              * Print it, skip to next file in archive.
  211.              */
  212.             PgmAlert(routine, "\pWrite short", macName);
  213.         doNext:
  214.             errFound = SkipFile((long)(size - written));
  215.             break;
  216.         }
  217.  
  218.         PBClose((ParmBlkPtr) &fpb, false);
  219.         fpb.fileParam.ioCompletion = nil;
  220.         fpb.fileParam.ioNamePtr = macName;
  221.         fpb.fileParam.ioVRefNum = dirVRefNum;
  222.         fpb.fileParam.ioDirID = 0;
  223.         fpb.fileParam.ioFVersNum = 0;
  224.         fpb.fileParam.ioFDirIndex = 0;
  225.         if (PBHGetFInfo(&fpb, false)) {
  226.             OSAlert(routine, "\pPBHGetFInfo", macName,
  227.                     fpb.fileParam.ioResult);
  228.             break;
  229.         }
  230.  
  231.         memcpy(&fpb.fileParam.ioFlFndrInfo.fdCreator, pref.creator, 4);
  232.         memcpy(&fpb.fileParam.ioFlFndrInfo.fdType, pref.type, 4);
  233.         fpb.fileParam.ioCompletion = nil;
  234.         fpb.fileParam.ioNamePtr = macName;
  235.         fpb.fileParam.ioVRefNum = dirVRefNum;
  236.         fpb.fileParam.ioDirID = 0;
  237.         fpb.fileParam.ioFVersNum = 0;
  238.         fpb.fileParam.ioFlMdDat = hstat.st_mtime + TIMEDIFF;
  239.         if (PBHSetFInfo(&fpb, false)) {
  240.             OSAlert(routine, "\pPBHSetFInfo", macName,
  241.                     fpb.fileParam.ioResult);
  242.             break;
  243.         }
  244.  
  245.         break;
  246.  
  247.     case LF_DIR:
  248.         /* Check for trailing / */
  249.         namelen = strlen(head->header.name)-1;
  250.     really_dir:
  251.         while (namelen && head->header.name[namelen] == '/')
  252.             head->header.name[namelen--] = '\0';    /* Zap / */
  253.         
  254.         FixName(head->header.name, macName);
  255.         /* FALL THROUGH */
  256.     again_dir:
  257.         memset(&dpb, 0, sizeof(dpb));
  258.         dpb.fileParam.ioCompletion = nil;
  259.         dpb.fileParam.ioNamePtr = macName;
  260.         dpb.fileParam.ioVRefNum = dirVRefNum;
  261.         dpb.fileParam.ioFVersNum = 0;
  262.         dpb.fileParam.ioDirID = 0;
  263.         err = PBDirCreate(&dpb, false);
  264.         if ((err != noErr) && (err != dupFNErr)) {
  265.             if (MakeDirs(macName))
  266.                 goto again_dir;
  267.  
  268.             PgmAlert(routine, "\pCould not make directory", macName);
  269.         }
  270.         
  271.         break;
  272.     }
  273.  
  274.     /* We don't need to save it any longer. */
  275.     SaveRec((union record **) 0);
  276.     return(errFound);
  277. }
  278.  
  279. /*
  280.  * After a file/link/symlink/dir creation has failed, see if
  281.  * it's because some required directory was not present, and if
  282.  * so, create all required dirs.
  283.  */
  284. int
  285. MakeDirs(pathname)
  286. char *pathname;
  287. {
  288.     int        madeone = 0;    /* Did we do anything yet? */
  289.     int        i, savedLen;
  290.     OSErr        err;
  291.     HParamBlockRec    pb;
  292.  
  293.     savedLen = pathname[0] & 0xff;
  294.     /*
  295.      * skip initial ':'
  296.      *
  297.      * Note that the directory name has already been converted to Mac style.
  298.      */
  299.     for (i = 2; i < savedLen; i++) {
  300.         while ((i < savedLen) && (pathname[i] != ':'))
  301.             i++;
  302.  
  303.         if (i == savedLen)
  304.             break;
  305.  
  306.         pathname[0] = i++ - 1;
  307.         memset(&pb, 0, sizeof(pb));
  308.         pb.fileParam.ioCompletion = nil;
  309.         pb.fileParam.ioNamePtr = pathname;
  310.         pb.fileParam.ioVRefNum = dirVRefNum;
  311.         pb.fileParam.ioFVersNum = 0;
  312.         pb.fileParam.ioDirID = 0;
  313.         err = PBDirCreate(&pb, false);
  314.         if (err == dupFNErr) {
  315.             continue;
  316.  
  317.         } else if (err != noErr) {
  318.             OSAlert("\pMakeDirs", "\pPBDirCreate", pathname,
  319.                     pb.fileParam.ioResult);
  320.             return(0);
  321.  
  322.         } else {
  323.             madeone++;        /* Remember if we made one */
  324.             continue;
  325.         }
  326.     }
  327.  
  328.     pathname[0] = savedLen;
  329.     return(madeone);        /* Tell them to retry if we made one */
  330. }
  331.  
  332. /*
  333.  * FixName - convert to a Mac style pathname
  334.  *
  335.  *    Conversions:
  336.  *        .    ->        (Stay at this directory level)
  337.  *        ..    -> ::        (Up a directory level)
  338.  *        .xxxx    -> _xxxx    (Don't get in trouble with device names)
  339.  *        xx:xx    -> xx/xx    (Don't get in trouble with directory delims)
  340.  */
  341. FixName(tar, mac)
  342. register char    *tar;
  343. char    *mac;
  344. {
  345.     char        *end = tar + strlen(tar);
  346.     register char    *p = mac + 1;
  347.     register char    *next;
  348.  
  349.     for (next = tar; tar < end; next++) {
  350.         /*
  351.          * Swallow up all contiguous '/' characters.
  352.          */
  353.         while (*next && (*next == '/'))
  354.             next++;
  355.  
  356.         /*
  357.          * Find the entire name up until the next '/'.
  358.          */
  359.         tar = next;
  360.         while (*next && (*next != '/'))
  361.             next++;
  362.  
  363.         *next = 0;
  364.         *p++ = ':';
  365.         if (*tar == '.')
  366.             switch (*(tar + 1)) {
  367.             case '\0':
  368.                 p--;
  369.                 continue;
  370.  
  371.             case '.':
  372.                 if (*(tar + 2) == 0)
  373.                     continue;
  374.                 /* else FALL THROUGH */
  375.  
  376.             default:
  377.                 *tar = '_';
  378.             }
  379.  
  380.         while (tar < next) {
  381.             if (*tar == ':')
  382.                 *tar = '/';
  383.             *p++ = *tar++;
  384.         }
  385.     }
  386.  
  387.     *mac = p - mac - 1;
  388. }
  389.  
  390. Boolean
  391. NewName(char *name, int retries)
  392. {
  393.     int    len = (int) *(unsigned char *)name;
  394.     
  395.     if ((len >= 253) || (retries >= 26))
  396.         return(false);
  397.         
  398.     if (retries == 0) {
  399.         *(unsigned char *)name += 2;
  400.         name[len + 1] = '-';
  401.         name[len + 2] = 'A';
  402.     
  403.     } else
  404.         name[len]++;
  405.     
  406.     return(true);
  407. }